package com.yc.services;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.yc.bean.RedPacketActivity;
import com.yc.bean.UserRedPacketRecord;
import com.yc.mapper.RedPacketActivityMapper;
import com.yc.mapper.UserRedPacketRecordMapper;
import com.yc.web.DTO.RedPacketActivityDTO;
import com.yc.web.DTO.UserRedPacketRecordDTO;
import com.yc.web.controller.model.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedPacketServiceImpl implements RedPacketService {

    // Redis键前缀定义
    private static final String RED_PACKET_PREFIX = "red_packet:";
    private static final String RED_PACKET_LIST_PREFIX = "red_packet_list:";
    private static final String RED_PACKET_USER_PREFIX = "red_packet_user:";

    @Autowired
    private RedPacketActivityMapper redPacketActivityMapper;

    @Autowired
    private UserRedPacketRecordMapper userRedPacketRecordMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 创建红包活动
     * @param totalAmount 总金额
     * @param totalCount 总数量
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 响应结果，包含创建的活动ID或错误信息
     */
    @Override
    @Transactional
    public ResponseResult createRedPacketActivity(Double totalAmount, Integer totalCount,
                                                  LocalDateTime startTime, LocalDateTime endTime) {
        try {
            // 1. 参数校验
            if (totalAmount <= 0 || totalCount <= 0) {
                return ResponseResult.error("红包总金额和数量必须大于0");
            }

            if (startTime.isAfter(endTime)) {
                return ResponseResult.error("活动开始时间不能晚于结束时间");
            }

            // 2. 创建红包活动实体
            RedPacketActivity activity = new RedPacketActivity();
            activity.setTotalAmount(totalAmount);
            activity.setTotalCount(totalCount);
            activity.setGrabbedCount(0);
            activity.setStartTime(startTime);
            activity.setEndTime(endTime);
            activity.setStatus(1); // 1-未开始
            activity.setVersion(0);

            // 3. 保存到数据库
            redPacketActivityMapper.insert(activity);

            // 4. 预生成红包金额列表并存入Redis
            List<Double> redPacketList = generateRedPacketList(totalAmount, totalCount);
            String listKey = RED_PACKET_LIST_PREFIX + activity.getActivityId();
            redisTemplate.opsForList().rightPushAll(listKey, redPacketList.toArray());

            // 设置Redis过期时间为活动结束时间
            long expireSeconds = LocalDateTime.now().until(endTime, ChronoUnit.SECONDS);
            redisTemplate.expire(listKey, expireSeconds, TimeUnit.SECONDS);

            // 5. 返回成功响应
            return ResponseResult.ok("红包活动创建成功").setData(activity.getActivityId());
        } catch (Exception e) {
            log.error("创建红包活动失败", e);
            return ResponseResult.error("创建红包活动失败: " + e.getMessage());
        }
    }

    /**
     * 获取红包活动详情
     * @param activityId 活动ID
     * @return 响应结果，包含活动详情或错误信息
     */
    @Override
    public ResponseResult getRedPacketActivity(Long activityId) {
        try {
            // 1. 查询数据库
            RedPacketActivity activity = redPacketActivityMapper.selectById(activityId);
            if (activity == null) {
                return ResponseResult.error("红包活动不存在");
            }

            // 2. 转换为DTO
            RedPacketActivityDTO dto = new RedPacketActivityDTO();
            BeanUtils.copyProperties(activity, dto);

            // 3. 返回成功响应
            return ResponseResult.ok().setData(dto);
        } catch (Exception e) {
            log.error("获取红包活动详情失败", e);
            return ResponseResult.error("获取红包活动详情失败: " + e.getMessage());
        }
    }

    /**
     * 获取用户红包领取记录
     * @param userId 用户ID
     * @param activityId 活动ID
     * @return 响应结果，包含领取记录或错误信息
     */
    @Override
    public ResponseResult getUserRedPacketRecord(Long userId, Long activityId) {
        try {
            // 1. 查询数据库
            QueryWrapper<UserRedPacketRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userId", userId).eq("activityId", activityId);
            UserRedPacketRecord record = userRedPacketRecordMapper.selectOne(queryWrapper);

            if (record == null) {
                return ResponseResult.error("未找到该用户的红包领取记录");
            }

            // 2. 转换为DTO
            UserRedPacketRecordDTO dto = new UserRedPacketRecordDTO();
            BeanUtils.copyProperties(record, dto);

            // 3. 返回成功响应
            return ResponseResult.ok().setData(dto);
        } catch (Exception e) {
            log.error("获取用户红包记录失败", e);
            return ResponseResult.error("获取用户红包记录失败: " + e.getMessage());
        }
    }

    /**
     * 抢红包
     * @param activityId 红包活动ID
     * @param userId 用户ID
     * @return 响应结果，包含抢到的红包金额或错误信息
     */
    @Override
    public ResponseResult grabRedPacket(Long activityId, Long userId) {
        try {
            // 1. 检查用户是否已经抢过红包
            if (hasGrabRedPacket(activityId, userId)) {
                return ResponseResult.error("您已经抢过该红包了");
            }

            // 2. 检查活动状态
            RedPacketActivity activity = checkActivityStatus(activityId);
            if (activity == null) {
                return ResponseResult.error("红包活动不存在或已结束");
            }

            // 3. 从Redis中获取红包金额
            Double amount = getRedPacketFromRedis(activityId);
            if (amount == null) {
                return ResponseResult.error("红包已被抢光");
            }

            // 4. 记录领取信息
            recordRedPacketGrab(activityId, userId, amount);

            // 5. 返回成功响应
            return ResponseResult.ok("抢红包成功").setData(amount);
        } catch (Exception e) {
            log.error("抢红包失败", e);
            return ResponseResult.error("抢红包失败: " + e.getMessage());
        }
    }

    /**
     * 检查用户是否已经抢过红包
     * @param activityId 活动ID
     * @param userId 用户ID
     * @return true-已抢过，false-未抢过
     */
    private boolean hasGrabRedPacket(Long activityId, Long userId) {
        String userKey = RED_PACKET_USER_PREFIX + activityId;
        return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(userKey, userId.toString()));
    }

    /**
     * 检查活动状态是否可抢
     * @param activityId 活动ID
     * @return 活动实体
     * @throws RuntimeException 活动不可用时会抛出异常
     */
    private RedPacketActivity checkActivityStatus(Long activityId) throws RuntimeException {
        RedPacketActivity activity = redPacketActivityMapper.selectById(activityId);
        if (activity == null) {
            throw new RuntimeException("红包活动不存在");
        }

        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(activity.getStartTime())) {
            throw new RuntimeException("红包活动尚未开始");
        }

        if (now.isAfter(activity.getEndTime())) {
            throw new RuntimeException("红包活动已结束");
        }

        if (activity.getStatus() != 2) { // 2-进行中
            throw new RuntimeException("红包活动不在进行中");
        }

        return activity;
    }

    /**
     * 从Redis中获取一个红包金额
     * @param activityId 活动ID
     * @return 红包金额，如果已抢光则返回null
     */
    private Double getRedPacketFromRedis(Long activityId) {
        String listKey = RED_PACKET_LIST_PREFIX + activityId;
        return (Double) redisTemplate.opsForList().leftPop(listKey);
    }

    /**
     * 记录用户抢红包信息（Redis原子操作+异步更新数据库）
     * @param activityId 活动ID
     * @param userId 用户ID
     * @param amount 抢到的金额
     */
    private void recordRedPacketGrab(Long activityId, Long userId, Double amount) {
        String userKey = RED_PACKET_USER_PREFIX + activityId;

        // 使用Lua脚本保证原子性操作：1.记录用户ID 2.增加已抢数量
        String luaScript = "if redis.call('sadd', KEYS[1], ARGV[1]) == 1 then " +
                "return redis.call('incr', KEYS[2]) " +
                "else return 0 end";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = redisTemplate.execute(redisScript,
                Arrays.asList(userKey, RED_PACKET_PREFIX + activityId + ":count"),
                userId.toString());

        if (result == 0) {
            throw new RuntimeException("您已经抢过该红包了");
        }

        // 异步更新数据库
        updateRedPacketActivityInDB(activityId, amount);
        createUserRedPacketRecord(activityId, userId, amount);
    }

    /**
     * 异步更新红包活动信息（减少金额和增加已抢数量）
     */
    @Async
    protected void updateRedPacketActivityInDB(Long activityId, Double amount) {
        try {
            UpdateWrapper<RedPacketActivity> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("activityId", activityId)
                    .setSql("grabbedCount = grabbedCount + 1")
                    .setSql("totalAmount = totalAmount - " + amount)
                    .set("updateTime", LocalDateTime.now());

            redPacketActivityMapper.update(null, updateWrapper);
        } catch (Exception e) {
            log.error("更新红包活动信息失败", e);
        }
    }

    /**
     * 异步创建用户红包领取记录
     */
    @Async
    protected void createUserRedPacketRecord(Long activityId, Long userId, Double amount) {
        try {
            UserRedPacketRecord record = new UserRedPacketRecord();
            record.setUserId(userId);
            record.setActivityId(activityId);
            record.setGrabAmount(amount);
            record.setGrabTime(LocalDateTime.now());

            userRedPacketRecordMapper.insert(record);
        } catch (Exception e) {
            log.error("创建用户红包记录失败", e);
        }
    }

    /**
     * 生成红包金额列表 (使用二倍均值法)
     * @param totalAmount 总金额
     * @param totalCount 总数量
     * @return 红包金额列表
     */
    private List<Double> generateRedPacketList(Double totalAmount, Integer totalCount) {
        List<Double> amountList = new ArrayList<>(totalCount);
        Double remainingAmount = totalAmount;
        Integer remainingCount = totalCount;
        Random random = new Random();

        for (int i = 0; i < totalCount - 1; i++) {
            // 二倍均值法计算当前红包金额
            Double max = remainingAmount / remainingCount * 2;
            Double amount = new BigDecimal(random.nextDouble() * max)
                    .setScale(2, RoundingMode.HALF_UP)
                    .doubleValue();

            amountList.add(amount);
            remainingAmount -= amount;
            remainingCount--;
        }

        // 最后一个红包
        amountList.add(new BigDecimal(remainingAmount).setScale(2, RoundingMode.HALF_UP).doubleValue());

        // 打乱顺序
        Collections.shuffle(amountList);

        return amountList;
    }
}
